package com.quick.develop.flowable.schedule;

import cn.hutool.core.collection.CollUtil;
import com.quick.develop.framework.utils.DateUtils;
import com.quick.develop.flowable.framework.factory.FlowServiceFactory;
import com.quick.develop.flowable.schedule.core.BpmScheduleHandler;
import com.quick.develop.flowable.service.task.IBpmVariableService;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.history.HistoricProcessInstance;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.time.LocalDate;
import java.util.*;

import static com.quick.develop.flowable.enums.BpmErrorCodeConstants.VAR_DELETE_ERROR;
import static com.quick.develop.framework.utils.DateUtils.YYYY_MM_DD_HH_MM_SS;

/**
 * desc: 定期清理非必须的历史参数 数据
 *
 * @author junyuan.liu
 * @date 2023/6/16 15:40
 */
@Slf4j
@Component
public class BpmVariableCleanUp extends FlowServiceFactory implements BpmScheduleHandler {
    
    @Resource
    private IBpmVariableService bpmVariableService;
    
    @Value("${flowable.custom.var-clean.finished-before:#{null}}")
    @DateTimeFormat(pattern = YYYY_MM_DD_HH_MM_SS)
    private Date finishedBefore;

    @Value("${flowable.custom.var-clean.finished-after:#{null}}")
    @DateTimeFormat(pattern = YYYY_MM_DD_HH_MM_SS)
    private Date finishedAfter;
    
    @Value("${flowable.custom.var-clean.names:#{null}}")
    private Set<String> varNames;

    @Value("${flowable.custom.var-clean.need-names:#{null}}")
    private Set<String> needVarNames;

    @Value("${flowable.custom.var-clean.keep-model:#{true}}")
    private boolean keepModel;
    
    private static final String[] SET_VALUES = new String[] { 
            "unitCodeEx1", "securityLevel", "unitNameSponsor", "isSectionChief",
            "formSource", "nextProess", "programType", "fileLevel",
            "unitNameEx1", "type", "typeNameEx1", "unitCodeEx2",
            "isGeneralManagerApply", "isAssistLeaderApply", "extendColumn3", "MultiResult",
            "assignee", "assigneeList", "distributeGroup", "userRy",
            "userKs", "userBm", "workArea", "isReplyLetter",
            "unitNameEx2", "communicationType", "multiResult", "text",
            "donation", "businessType", "countersignDepartment", "distributeGroup",
            "enableDist", "formSource", "ksFlag", "targetType",
            "title", "userBm", "userKs", "userRy",
            "executeCmd", "cmd", "entrustRelease", "entrustAssignee",
            "entrustOwner", "entrustRelease", "entrustIgnore",
            "businessKey", "cancelAutoCompete", "isSign", "withdrawUser"
    };

    private static final String[] NEED_SET_VALUES = new String[] {
            "INITIATOR", "INITIATOR_NAME", "formCode", "processName"
    };

    private static final Set<String> DEFAULT_VAR = new HashSet<>(Arrays.asList(SET_VALUES));

    private static final Set<String> DEFAULT_NEED_VAR = new HashSet<>(Arrays.asList(NEED_SET_VALUES));
    
    private Set<String> varCleanUpNames;

    private Set<String> needKeepVarNames;
    
    
    @Override
    @Async
    public void execute() {
        LocalDate localDate = LocalDate.now();
        if (Objects.isNull(finishedBefore)) {
            finishedBefore = DateUtils.toDate(localDate.plusWeeks(-1)) ;
        }
        if (Objects.isNull(finishedAfter)) {
            finishedAfter = DateUtils.toDate(localDate.plusWeeks(-2)) ;
        }
        varCleanUpNames = new HashSet<>();
        if (CollUtil.isEmpty(varNames)) {
            varCleanUpNames = DEFAULT_VAR;
        } else {
            varCleanUpNames.addAll(varNames);
            varCleanUpNames.addAll(DEFAULT_VAR);
        }

        needKeepVarNames = new HashSet<>();
        if (CollUtil.isEmpty(needVarNames)) {
            needKeepVarNames = DEFAULT_NEED_VAR;
        } else {
            needKeepVarNames.addAll(needVarNames);
            needKeepVarNames.addAll(DEFAULT_NEED_VAR);
        }


        List<HistoricProcessInstance> instanceList = getMainEndProcesses();
        for (HistoricProcessInstance instance : instanceList) {
            deleteProcVarByMain(instance);
        }
    }
    
    /** 
     * @description 获取已结束的主流程信息
     *  
     * @return java.util.List<org.flowable.engine.history.HistoricProcessInstance>
     * @author junyuan.liu
     * @date 2023/6/19 15:11
     */ 
    private List<HistoricProcessInstance> getMainEndProcesses() {
        return historyService.createHistoricProcessInstanceQuery()
                .finishedAfter(finishedAfter)
                .finishedBefore(finishedBefore)
                .excludeSubprocesses(true)
                .orderByProcessInstanceEndTime().asc()
                .list();
    }
    
    private List<HistoricProcessInstance> getSubProcesses(String id) {
        return historyService.createHistoricProcessInstanceQuery()
                .superProcessInstanceId(id)
                .finished()
                .orderByProcessInstanceEndTime().asc()
                .list();
    }
    
    
    private void deleteProcVarByMain(HistoricProcessInstance instance) {
        try {
            List<HistoricProcessInstance> subList = getSubProcesses(instance.getId());
            if (CollUtil.isNotEmpty(subList)) {
                for (HistoricProcessInstance item : subList) {
                    deleteProcVarByMain(item);
                }
            }

            if (keepModel) {
                deleteNotNeedVar(instance.getId());
            }else {
                deleteVar(instance.getId());
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    /** 
     * @description 
     * @param procInstId 清理流程参数
     * @author junyuan.liu
     * @date 2023/6/19 15:11
     */ 
    private void deleteVar(String procInstId) {
        try {
            bpmVariableService.deleteHisVar(procInstId, varCleanUpNames);
        } catch (Exception e) {
            log.error(VAR_DELETE_ERROR.getMsg(), procInstId, e);
            e.printStackTrace();
        }
    }

    private void deleteNotNeedVar(String procInstId) {
        try {
            bpmVariableService.deleteHisNotNeedVar(procInstId, needKeepVarNames);
        } catch (Exception e) {
            log.error(VAR_DELETE_ERROR.getMsg(), procInstId, e);
            e.printStackTrace();
        }
    }
    
    
}
